Imports Clo = Enhancements.Core.Logging
Imports Ere = Enhancements.Basic.RegularExpressions
Imports Fun = Enhancements.Basic.Expressions.Functions
Imports Gen = System.Collections.Generic
Imports Glo = System.Globalization
Imports Iou = System.IO
Imports Lev = Enhancements.Basic.Expressions.Levels
Imports Ref = System.Reflection
Imports Reg = System.Text.RegularExpressions
Imports Sys = System
Imports Tex = System.Text
Imports Val = Enhancements.Basic.Expressions.Values
Imports Xsd = System.Xml.Schema
Imports W3c = System.Xml

Namespace Enhancements.Basic.Expressions
    ''' <summary>Represents a compiled expression that can used for solving.</summary>
    <Serializable(), DebuggerStepThrough()> _
    Public NotInheritable Class Expression

        ''' <exception cref="IsNullException"> </exception>
        ''' <exception cref="IsListFilledNot"> </exception>
        ''' <exception cref="CompilerExpressionRootTokenTypeIsValidNotException"> </exception>
        Friend Sub New(ByVal _expression As System.String, ByVal _rootToken As Token, ByVal _tokensVariables() As Variable)
            _tokensVariables.IsListFilled("_tokensVariables", True)

            __Expression = _expression
            RootToken = _rootToken
            Dim _variablesList As Gen.List(Of ExpressionVariable) = New Gen.List(Of ExpressionVariable)()
            For Each _tokenVariable As Variable In _tokensVariables
                _variablesList.Add(New ExpressionVariable(Me, _tokenVariable))
            Next _tokenVariable
            __Variables = _variablesList.ToArray()
        End Sub

        Private __Expression As System.String = Nothing

        Private __RootToken As Token = Nothing
        ''' <summary>Gets the root token that is the end result of the compilation process.</summary>
        Public Property RootToken() As Token
            Get
                Return __RootToken
            End Get
            Private Set(ByVal _value As Token)
                _value.IsNullNot("RootToken._value", True)
                If _value.GetType().IsDerivedOfNot("_value.GetType()", GetType(Val.Value), False) AndAlso _value.IsOfTypeNot("_value", GetType(Variable), False) AndAlso _value.IsOfTypeNot("_value", GetType(Fun.FunctionCall), False) Then
                    CompilerExpressionRootTokenTypeIsValidNotException.___Throw(__Expression, _value)
                End If

                __RootToken = _value
            End Set
        End Property

        Private __Variables() As ExpressionVariable = {}
        ''' <summary>Gets the variables in the expression.</summary>
        Public ReadOnly Property Variables() As ExpressionVariable()
            Get
                Return __Variables
            End Get
        End Property

        ''' <summary>Determines the expression's representation with all the operators already resolved to function calls.</summary>
        ''' <returns> </returns>
        Public Function ToRepresentation() As System.String
            Dim _returnValueStringBuilder As Tex.StringBuilder = New Tex.StringBuilder(Sys.String.Empty)
            __ToRepresentation(RootToken, _returnValueStringBuilder)

            Return _returnValueStringBuilder.ToString()
        End Function

        ''' <exception cref="IsNullException"> </exception>
        Private Sub __ToRepresentation(ByVal _token As Token, ByRef _result As Tex.StringBuilder)
            _token.IsNullNot("_token", True)
            _result.IsNullNot("_result", True)

            If _token.IsOfTypeNot("_token", GetType(Fun.FunctionCall), False) Then
                _result.Append(_token.ToRepresentation())
            Else
                Dim _tokenCasted As Fun.FunctionCall = CType(_token, Fun.FunctionCall)
                _result.Append(_tokenCasted.Function.Name)
                _result.Append(Lev.DelimiterBegin.Representation)
                For _counter As Sys.Int32 = 0 To _tokenCasted.Parameters.Count - 1
                    __ToRepresentation(_tokenCasted.Parameters(_counter), _result)
                    If _counter < _tokenCasted.Parameters.Count - 1 Then
                        _result.Append(Fun.FunctionParametersSeparator.Representation)
                    End If
                Next _counter
                _result.Append(Lev.DelimiterEnd.Representation)
            End If
        End Sub

        ''' <summary>Solves the expression according to the variables values specified, if any, and returns the end result.</summary>
        ''' <exception cref="ExpressionVariableValueIsValidNotException">At least one variable has a not valid value.</exception>
        ''' <exception cref="ExpressionFunctionCallSolvingException">A function call solving threw an exception.</exception>
        ''' <returns> </returns>
        Public Function Solve() As Sys.Object
            For Each _variable As ExpressionVariable In Variables
                If Assert.IsNull(_variable.Value, "_variable.Value", False) Then
                    ExpressionVariableValueIsValidNotException.___Throw(Me, _variable, _variable.Value)
                End If
            Next _variable

            Dim _returnValue As Sys.Object = Nothing
            If RootToken.GetType().IsDerivedOf("RootToken.GetType()", GetType(Val.Value), False) Then
                _returnValue = CType(RootToken, Val.Value).Value
            Else
                If RootToken.IsOfType("RootToken", GetType(Variable), False) Then
                    Dim _rootTokenCasted As Variable = CType(RootToken, Variable)
                    For Each _variable As ExpressionVariable In Variables
                        If Variable.___NamesReferToTheSameVariable(_rootTokenCasted.ExpressionPart, _variable.Name).IsTrue("Variable.___NamesReferToTheSameVariable(_rootTokenCasted.ExpressionPart,_variable.Name)", False) Then
                            _returnValue = _variable.Value
                            Exit For
                        End If
                    Next _variable
                Else
                    _returnValue = __SolveFunctionCall(CType(RootToken, Fun.FunctionCall))
                End If
            End If

            Return _returnValue
        End Function

        ''' <exception cref="ExpressionFunctionCallSolvingException"> </exception>
        Private Function __SolveFunctionCall(ByVal _functionCall As Fun.FunctionCall) As Sys.Object
            Dim _parametersList As Gen.List(Of Sys.Object) = New Gen.List(Of Sys.Object)()
            For Each _parameterToken As Token In _functionCall.Parameters
                If _parameterToken.GetType().IsDerivedOf("_parameterToken.GetType()", GetType(Val.Value), False) Then
                    _parametersList.Add(CType(_parameterToken, Val.Value).Value)
                Else
                    If _parameterToken.IsOfType("_parameterToken", GetType(Variable), False) Then
                        For Each _variable As ExpressionVariable In Variables
                            If Variable.___NamesReferToTheSameVariable(CType(_parameterToken, Variable).ExpressionPart, _variable.Name).IsTrue("Variable.___NamesReferToTheSameVariable(CType(_parameterToken, Variable).ExpressionPart, _variable.Name)", False) Then
                                _parametersList.Add(_variable.Value)
                                Exit For
                            End If
                        Next _variable
                    Else
                        _parametersList.Add(__SolveFunctionCall(CType(_parameterToken, Fun.FunctionCall)))
                    End If
                End If
            Next _parameterToken
            Dim _returnValue As Sys.Object = Nothing
            Try
                _returnValue = _functionCall.Function.Solve(_functionCall.FunctionSignature, _parametersList.ToArray())
            Catch _exception As ExpressionFunctionCallSolvingException
                Throw
            Catch _exception As Exception
                ExpressionFunctionCallSolvingException.___Throw(Me, _functionCall, _parametersList.ToArray(), _exception)
            End Try

            Return _returnValue
        End Function

    End Class
End Namespace
